package fastexec

import (
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
)

// Ping handles ping commands with concurrent ICMP requests.
// It parses the command (e.g., "ping -n 4 host") and executes concurrent pings.
func Ping(command string) (string, error) {
	parts := strings.Fields(command)
	if len(parts) < 2 || parts[0] != "ping" {
		return "", fmt.Errorf("invalid ping command")
	}

	count := 4 // default
	host := ""
	for i := 1; i < len(parts); i++ {
		if parts[i] == "-n" && i+1 < len(parts) {
			var err error
			count, err = strconv.Atoi(parts[i+1])
			if err != nil {
				return "", fmt.Errorf("invalid count: %w", err)
			}
			i++ // skip next
		} else {
			host = parts[i]
		}
	}

	if host == "" {
		return "", fmt.Errorf("no host specified")
	}

	ip, err := net.ResolveIPAddr("ip4", host)
	if err != nil {
		return "", err
	}

	c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
	if err != nil {
		return "", err
	}
	defer c.Close()

	var wg sync.WaitGroup
	results := make([]string, count)
	errs := make(chan error, count)
	timeout := 1 * time.Second

	for seq := 0; seq < count; seq++ {
		wg.Add(1)
		go func(seq int) {
			defer wg.Done()
			start := time.Now()
			msg := icmp.Message{
				Type: ipv4.ICMPTypeEcho,
				Code: 0,
				Body: &icmp.Echo{
					ID:   os.Getpid() & 0xffff,
					Seq:  seq,
					Data: []byte(""),
				},
			}
			b, err := msg.Marshal(nil)
			if err != nil {
				errs <- err
				return
			}
			if _, err := c.WriteTo(b, &net.IPAddr{IP: ip.IP}); err != nil {
				errs <- err
				return
			}
			rb := make([]byte, 1500)
			c.SetReadDeadline(time.Now().Add(timeout))
			n, _, err := c.ReadFrom(rb)
			if err != nil {
				errs <- err
				return
			}
			rm, err := icmp.ParseMessage(ipv4.ICMPTypeEcho.Protocol, rb[:n])
			if err != nil {
				errs <- err
				return
			}
			if rm.Type == ipv4.ICMPTypeEchoReply {
				rtt := time.Since(start)
				results[seq] = fmt.Sprintf("Reply from %s: seq=%d time=%v", host, seq, rtt)
			} else {
				errs <- fmt.Errorf("unexpected message type")
			}
		}(seq)
	}

	wg.Wait()
	close(errs)

	for err := range errs {
		if err != nil {
			return "", err
		}
	}

	return strings.Join(results, "\n"), nil
}
